package com.mitei.dao.beans.persistants;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

import com.mitei.dao.annotations.TABLE_ID;
import com.mitei.dao.annotations.TABLE_SEQ;
import com.mitei.dao.enumerations.QueryTypes;
import com.mitei.fwk.search.StringSearcher;

public class BeanMapper {
	
	private static String findBeanName(String query) {
		
		query = query.toUpperCase();
		String[] tokens = query.split(" ");
		int index = StringSearcher.indexOf(tokens, "FROM");
		return tokens[index+1];
	}
	
	@SuppressWarnings("unchecked")
	private static Bean createBeanInstance(String beanName) {
		
		try {
		    Class cl = Class.forName("com.mitei.dao.beans.persistants." + beanName);
		    java.lang.reflect.Constructor co = cl.getConstructor();
		    return (Bean) co.newInstance();
		} catch (Exception e) {
		    throw new InternalError();
		}
	}
	
	private static void injectToBean(Bean bean, Method method, String value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		
		Type[] types = method.getGenericParameterTypes();
		if (types.length == 1) {
			Type cl = types[0];
			if (cl.equals(Integer.class)) {
				method.invoke(bean, Integer.valueOf(value));
			} else if (cl.equals(Double.class)) {
				method.invoke(bean, Double.valueOf(value));
			} else if (cl.equals(String.class)) {
				method.invoke(bean, String.valueOf(value));
			}
		}
	}

	public static Bean resultSet2Bean(ResultSet resultSet, String query) throws SQLException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		
		String beanName = findBeanName(query);
		Bean bean = createBeanInstance(beanName);
		Method[] methods = bean.getClass().getDeclaredMethods();
		
		ResultSetMetaData rsmd = resultSet.getMetaData() ;
		int columnNumber = rsmd.getColumnCount();
		for (int i = 1 ; i <= columnNumber ; i++) {
			for(Method meth : methods) {
				if (meth.getName().toUpperCase().equals("SET" + rsmd.getColumnLabel(i).toUpperCase())) {
					injectToBean(bean, meth, resultSet.getString(i));
				}
			}
		}
		return bean;
	}
	
	public static String bean2Query(Bean bean, QueryTypes queryType) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		
		String query = null;
		
		if(queryType.equals(QueryTypes.UPDATE)) {
			query = bean2Update(bean);
		} else if(queryType.equals(QueryTypes.INSERT)) {
			query = bean2Insert(bean);
		} else if(queryType.equals(QueryTypes.DELETE)) {
			query = bean2Delete(bean);
		}
		
		return query;
	}
	
	private static String bean2Update(Bean bean) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		
		String updateQuery = "update " + bean.getClass().getSimpleName() + " set ";
		String identField = bean.getClass().getAnnotation(TABLE_ID.class).name().toUpperCase();
		String whereClause = " where " + identField + "=";
		
		Method[] methods = bean.getClass().getDeclaredMethods();
		for(Method meth : methods) {
			if (meth.getName().toUpperCase().startsWith("GET")
					&& !meth.getName().toUpperCase().endsWith(identField)) {
				updateQuery += getFieldFromMethod(meth.getName()) + "=" + getResultFromInvoke(bean, meth) + ",";
			} else if (meth.getName().toUpperCase().startsWith("GET")
					&& meth.getName().toUpperCase().endsWith(identField)) {
				whereClause += getResultFromInvoke(bean, meth);
			}
		}
		
		if(updateQuery.endsWith(",")) {
			int i = updateQuery.lastIndexOf(",");
			updateQuery = updateQuery.substring(0, i);
		}
		
		return updateQuery+whereClause;
	}
	
	private static String bean2Delete(Bean bean) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		
		String updateQuery = "delete from " + bean.getClass().getSimpleName();
		String identField = bean.getClass().getAnnotation(TABLE_ID.class).name().toUpperCase();
		String whereClause = " where " + identField + "=";
		
		Method[] methods = bean.getClass().getDeclaredMethods();
		for(Method meth : methods) {
			if (meth.getName().toUpperCase().startsWith("GET")
					&& meth.getName().toUpperCase().endsWith(identField)) {
				whereClause += getResultFromInvoke(bean, meth);
			}
		}
		
		return updateQuery+whereClause;
	}
	
	private static String getResultFromInvoke(Bean bean, Method method) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		
		Type returnType = method.getGenericReturnType();
		Object returnedValue = method.invoke(bean);
		
		if (returnType.equals(Integer.class)) {
			Integer value = (Integer) returnedValue;
			return value.toString();
		} else if (returnType.equals(Double.class)) {
			Double value = (Double) returnedValue;
			return value.toString();
		} else if (returnType.equals(String.class)) {
			String value = (String) returnedValue;
			return "'" + value + "'";
		}
		
		return null;
	}
	
	private static String getFieldFromMethod(String method) {
		
		method = StringSearcher.replaceTokenInString(method.toUpperCase(), "GET", "");
		
		return method;
	}
	
	private static String bean2Insert(Bean bean) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		
		String insertQuery1 = "insert into " + bean.getClass().getSimpleName() + "(";
		String insertQuery2 = " values (";
		String identField = bean.getClass().getAnnotation(TABLE_ID.class).name().toUpperCase();
		String sequenceName = bean.getClass().getAnnotation(TABLE_SEQ.class).name().toUpperCase();
		
		Method[] methods = bean.getClass().getDeclaredMethods();
		for(Method meth : methods) {
			if (meth.getName().toUpperCase().startsWith("GET")
					&& !meth.getName().toUpperCase().endsWith(identField)) {
				insertQuery1 += getFieldFromMethod(meth.getName()) + ",";
				insertQuery2 += getResultFromInvoke(bean, meth) + ",";
			} else if (meth.getName().toUpperCase().startsWith("GET")
					&& meth.getName().toUpperCase().endsWith(identField)) {
				insertQuery1 += getFieldFromMethod(meth.getName()) + ",";
				insertQuery2 += sequenceName + ".nextval,";
			}
		}
		
		if(insertQuery1.endsWith(",")) {
			int i = insertQuery1.lastIndexOf(",");
			insertQuery1 = insertQuery1.substring(0, i) + ")";
		}
		if(insertQuery2.endsWith(",")) {
			int i = insertQuery2.lastIndexOf(",");
			insertQuery2 = insertQuery2.substring(0, i) + ")";
		}
		
		return insertQuery1 + insertQuery2;
	}
	
}
